package com.satan.novel.monitor.config;

import de.codecentric.boot.admin.server.config.AdminServerProperties;
import jakarta.servlet.DispatcherType;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import java.util.UUID;

import static org.springframework.http.HttpMethod.DELETE;
import static org.springframework.http.HttpMethod.POST;

// 标记为配置类，Spring容器会自动扫描并处理其中的@Bean方法
@Configuration(proxyBeanMethods = false)
public class SecuritySecureConfig {

    // 注入AdminServerProperties实例，用于获取管理服务器相关路径信息
    private final AdminServerProperties adminServer;

    // 注入SecurityProperties实例，提供Spring Security的基本配置信息
    private final SecurityProperties security;

    // 构造函数，注入依赖项
    public SecuritySecureConfig(AdminServerProperties adminServer, SecurityProperties security) {
        this.adminServer = adminServer;
        this.security = security;
    }

    // 定义一个安全过滤器链的Bean
    @Bean
    protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        // 创建一个保存登录前请求的认证成功处理器
        SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
        successHandler.setTargetUrlParameter("redirectTo");
        successHandler.setDefaultTargetUrl(this.adminServer.path("/"));

        // 配置HTTP安全授权规则
        http.authorizeHttpRequests((authorizeRequests) -> authorizeRequests //
                .requestMatchers(new AntPathRequestMatcher(this.adminServer.path("/assets/**"))).permitAll() // 允许访问静态资源
                .requestMatchers(new AntPathRequestMatcher(this.adminServer.path("/actuator/info"))).permitAll() // 允许访问服务信息
                .requestMatchers(new AntPathRequestMatcher(adminServer.path("/actuator/health"))).permitAll() // 允许访问健康检查
                .requestMatchers(new AntPathRequestMatcher(this.adminServer.path("/login"))).permitAll() // 允许访问登录页面
                .dispatcherTypeMatchers(DispatcherType.ASYNC).permitAll() // 允许异步请求类型不受限制
                .anyRequest().authenticated()); // 其他所有请求需要经过身份验证

        // 配置表单登录支持
        http.formLogin((formLogin) -> formLogin.loginPage(this.adminServer.path("/login"))
                .successHandler(successHandler));

        // 配置登出功能
        http.logout((logout) -> logout.logoutUrl(this.adminServer.path("/logout")));

        // 配置HTTP基本认证支持
        http.httpBasic(Customizer.withDefaults());

        // 添加自定义CSRF过滤器到过滤器链中
        http.addFilterAfter(new CustomCsrfFilter(), BasicAuthenticationFilter.class);

        // 配置CSRF保护机制
        http.csrf((csrf) -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                .csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler()).ignoringRequestMatchers(
                        new AntPathRequestMatcher(this.adminServer.path("/instances"), POST.toString()),
                        new AntPathRequestMatcher(this.adminServer.path("/instances/*"), DELETE.toString()),
                        new AntPathRequestMatcher(this.adminServer.path("/actuator/**")))); // 忽略指定URL的CSRF保护

        // 开启记住我功能，并设置密钥和有效时间
        http.rememberMe((rememberMe) -> rememberMe.key(UUID.randomUUID().toString()).tokenValiditySeconds(1209600));

        // 构建并返回最终的安全过滤器链
        return http.build();

    }

    // 为了实现"记住我"功能，创建一个内存中的UserDetailsService Bean
    @Bean
    public InMemoryUserDetailsManager userDetailsService(PasswordEncoder passwordEncoder) {
        // 创建一个默认用户，并使用BCryptPasswordEncoder加密密码
        UserDetails user = User.withUsername("admin").password(passwordEncoder.encode("123456")).roles("USER").build();
        return new InMemoryUserDetailsManager(user);
    }

    // 创建一个BCryptPasswordEncoder密码编码器Bean
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}
